package prj3;


import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;

import prj3.ui.ColorPalettePanel;
import prj3.ui.EarthDisplay;
import util.Conversions;
import util.Profiler;
import util.Constants;

public class EarthSim extends JFrame implements Runnable {
    
    EarthGrid grid = null;
    protected EarthDisplay display;
    public JTextField simulatedTime;
    protected int sunPosition;
    protected JTextField gridSpacing;
    protected JTextField simulationTimeStepField;
    protected Container contentPane;
    protected JPanel controlPanel;
    protected JButton startButton;
    protected JButton stopButton;
    protected JButton restartButton;
    protected JButton pauseButton;
    protected JPanel settingsPanel;
    protected JTextField presentationRate;
    protected JSlider presentationRateSlider;
    protected JPanel buttonPanel;
    protected ColorPalettePanel colorPalettePanel;
    
    protected JLabel simulatedTimeLabel = new JLabel("Simulated Date");
    protected JLabel simulationTimeStepLabel = new JLabel("Simulation Timestep");
    protected JLabel gridSpacingLabel = new JLabel("Grid Spacing");
    protected JLabel presentationRateLabel = new JLabel("Animation Speed (%)");
    protected JLabel errorMsg = new JLabel();
    protected int runState;
   
    
    public int initiative;
    public boolean simulationThread = false;
    public boolean presentationThread = false;
    protected int simulationTimeStep = 0;
    
    int maxTemp = 30;
    
    
    public EarthSim(int initiative, boolean simulationThread, boolean presentationThread){
        this.initiative = initiative;
        this.simulationThread = simulationThread;
        this.presentationThread = presentationThread;
        setSize(800,600);
        
        this.setNativeLookAndFeel();
        contentPane = this.getContentPane();
        initComponents();
        
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocation(0,0);
        setVisible(true);

    }
    
    public void run(){
    	//Profiler p = Profiler.getInstance();
    	//p.start();
        if(runState == Constants.RUNNING){

            grid.setStepSize(simulationTimeStep);
            if(initiative == Constants.PUSH){
                grid.setDisplay(display);
                if(simulationThread || presentationThread){
                    new Thread(grid).start(); 
                 }
                int displayCounter = 0;
                
                while(runState == Constants.RUNNING){
                    if(!simulationThread && !presentationThread){
                        grid.stepGrid(simulationTimeStep);
                    }
                    int speedSlider = presentationRateSlider.getValue();
                    
                    if(!simulationThread && !presentationThread){
                        try {
                            Thread.sleep((101-speedSlider));
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    } else if(simulationThread || presentationThread){
                        try {
                            Thread.sleep((101-speedSlider) * 10);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                    displayCounter++;
                }
                grid.setRunState(runState);
            } else if(initiative == Constants.PULL){
                if(simulationThread || presentationThread){
                    new Thread(grid).start(); 
                 }
                int displayCounter = 0;
                while(runState == Constants.RUNNING){
                    if(!simulationThread && !presentationThread){
                        grid.stepGrid(simulationTimeStep);
                    }
                    int speedSlider = presentationRateSlider.getValue();
                    if(!simulationThread && !presentationThread && displayCounter % (101-speedSlider) == 0){
                        display.updateDisplay((EarthGrid)grid);
                        //display.repaint();
                    } else if(simulationThread || presentationThread){
                        display.updateDisplay((EarthGrid)grid);
                        //display.repaint();
                        try {
                            Thread.sleep((101-speedSlider) * 10);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                    displayCounter++;
                }
                grid.setRunState(runState);
            } else if(initiative == Constants.BUFFER){
                if(simulationThread || presentationThread){
                    new Thread(grid).start(); 
                 }
                int displayCounter = 0;
                while(runState == Constants.RUNNING){
                    if(!simulationThread && !presentationThread){
                        grid.stepGrid(simulationTimeStep);
                    }
                    int speedSlider = presentationRateSlider.getValue();
                    
                    if(!simulationThread && !presentationThread && displayCounter % (101-speedSlider) == 0){
                        display.updateDisplay(grid);
                    	//display.repaint();
                    } else if(simulationThread || presentationThread){
                    	display.updateDisplay(grid);
                        //display.repaint();
                        try {
                            Thread.sleep((101-speedSlider) * 10);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                    displayCounter++;
                }
                grid.setRunState(runState);
            }
        }
    }
    
    

    private void initComponents(){
        display = new EarthDisplay(null, this, maxTemp);
        simulatedTime = new JTextField("");
        simulatedTime.setEditable(false);
        
        gridSpacingLabel.setHorizontalAlignment(javax.swing.JTextField.CENTER);
        simulationTimeStepLabel.setHorizontalAlignment(javax.swing.JTextField.CENTER);
        presentationRateLabel.setHorizontalAlignment(javax.swing.JTextField.CENTER);
        simulatedTimeLabel.setHorizontalAlignment(javax.swing.JTextField.CENTER);
        
        gridSpacing = new JTextField("15");
        simulationTimeStepField = new JTextField("1");
        presentationRateSlider = new JSlider(JSlider.HORIZONTAL, 0, 100, 100);
        presentationRateSlider.setMajorTickSpacing(10);
        presentationRateSlider.setPaintTicks(true);
        presentationRateSlider.setPaintTrack(true);
        presentationRateSlider.setPaintLabels(true);
        
        startButton = new JButton("Start");
        stopButton = new JButton("Stop");
        pauseButton = new JButton("Pause");
        restartButton = new JButton("Restart");
        ButtonListener buttonListener = new ButtonListener(this);
        startButton.addActionListener(buttonListener);
        stopButton.addActionListener(buttonListener);
        pauseButton.addActionListener(buttonListener);
        restartButton.addActionListener(buttonListener);
        setRunState(Constants.STOPPED);
        colorPalettePanel = new ColorPalettePanel(maxTemp);
        controlPanel = new JPanel(new GridLayout(3,1));

        settingsPanel = new JPanel(new GridLayout(2, 5));
        buttonPanel = new JPanel(new FlowLayout());
        
        settingsPanel.add(gridSpacingLabel);
        settingsPanel.add(simulationTimeStepLabel);
        settingsPanel.add(presentationRateLabel);
        settingsPanel.add(simulatedTimeLabel);
        
        settingsPanel.add(gridSpacing);
        settingsPanel.add(simulationTimeStepField);
        settingsPanel.add(presentationRateSlider);
        settingsPanel.add(simulatedTime);
        
        buttonPanel.add(startButton);
        buttonPanel.add(pauseButton);
        buttonPanel.add(restartButton);
        buttonPanel.add(stopButton);
        

        //controlPanel.add(colorPalettePanel); 
        controlPanel.add(settingsPanel);
        controlPanel.add(buttonPanel);
        controlPanel.add(errorMsg);
       

        contentPane.setLayout(new BorderLayout());
        contentPane.add(colorPalettePanel, BorderLayout.WEST);
        contentPane.add(display, BorderLayout.CENTER);
        contentPane.add(controlPanel, BorderLayout.SOUTH);
        
        
        
        
        
    }
    
    private void setNativeLookAndFeel()
    {
        try
        {
            javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager
                    .getSystemLookAndFeelClassName());
        } catch (Exception e)
        {
            System.out.println("Error setting native LAF: " + e);
        }
    }
    
    
    
    
    public static void main(String args[]){
        boolean simulationThread = false;
        boolean presentationThread = false;
        int initiative = 0;
        boolean gotInitiative = false;
        for(int i = 0; i < args.length; i++){
            if(args[i].equals("-s")){
                simulationThread = true;
            } else if(args[i].equals("-p")){
                presentationThread = true;
            } else if(args[i].equals("-t")){
                initiative = Constants.PUSH;
                gotInitiative = true;
            } else if(args[i].equals("-r")){
                initiative = Constants.PULL;
                gotInitiative = true;
            } else if(args[i].equals("-b")){
                initiative = Constants.BUFFER;
                gotInitiative = true;
            }
        }
        if(!gotInitiative){
            System.err.println("ERROR: You must at least specify exactly 1 initiative.");
            System.exit(-1);
        }
       
        EarthSim s = new EarthSim(initiative, simulationThread, presentationThread);
    }
    
    private class ButtonListener implements ActionListener {
        EarthSim sim;
        public ButtonListener(EarthSim sim){
            this.sim = sim;
        }
        
        public void actionPerformed(ActionEvent e) {
            Object source = e.getSource();
            if(source.equals(sim.startButton)){
                int gridSpacing;
                try{
                    gridSpacing = Integer.parseInt(sim.gridSpacing.getText());
                } catch(NumberFormatException e1){
                    sim.errorMsg.setText("ERROR: Grid Spacing must be an Integer between 1 and 180 inclusive");
                    sim.setRunState(Constants.STOPPED);
                    return;
                }
                if(gridSpacing < 1 || gridSpacing > 180){
                    sim.errorMsg.setText("ERROR: Grid Spacing must be an Integer between 1 and 180 inclusive");
                    sim.setRunState(Constants.STOPPED);
                    return;
                }
                
                try{
                    sim.simulationTimeStep = Integer.parseInt(sim.simulationTimeStepField.getText());
                } catch(NumberFormatException e1){
                    sim.errorMsg.setText("ERROR: Simulation Time Step must be an Integer between 1 and 1440 (1 day) inclusive");
                    sim.setRunState(Constants.STOPPED);
                    return;
                }
                 
                if(simulationTimeStep < 1 || simulationTimeStep > 1440){
                    sim.errorMsg.setText("ERROR: Simulation Time Step must be an Integer between 1 and 1440 (1 day) inclusive");
                    sim.setRunState(Constants.STOPPED);
                    return;
                }
                sim.setRunState(Constants.RUNNING);
                if(sim.initiative != Constants.BUFFER){
                    sim.grid = new EarthGrid(6378, (float)Conversions.degToRad(gridSpacing), (float)0.0167, (float)(Math.PI * (23.0/180.0)));
                } else {
                    sim.grid = new BufferedEarthGrid(6378, (float)Conversions.degToRad(gridSpacing), (float)0.0167, (float)(Math.PI * (23.0/180.0)), Constants.maxBufferSize);
                }
                sim.display.updateDisplay(grid);
                new Thread(sim).start();
            } else if(source.equals(sim.stopButton)){
                sim.setRunState(Constants.STOPPED);
                // TODO send simulation engine kill signal
            } else if(source.equals(sim.pauseButton)){
                sim.setRunState(Constants.PAUSED);
                // TODO send simulation engine pause signal
            } else if(source.equals(sim.restartButton)){
                sim.setRunState(Constants.RUNNING);
                new Thread(sim).start();
            }
        }
        
    
    }
    
    protected void setRunState(int state){
        this.runState = state;
        if(state == Constants.RUNNING){
            startButton.setEnabled(false);
            stopButton.setEnabled(true);
            restartButton.setEnabled(false);
            pauseButton.setEnabled(true);
        } else if(state == Constants.STOPPED){
            startButton.setEnabled(true);
            stopButton.setEnabled(false);
            restartButton.setEnabled(false);
            pauseButton.setEnabled(false);
            
        } else if(state == Constants.PAUSED){
            startButton.setEnabled(false);
            stopButton.setEnabled(true);
            restartButton.setEnabled(true);
            pauseButton.setEnabled(false);
        }
    }
}
